
"""
“核技巧”(Kernel Trick)，是一种能够使用数据原始空间中的向量计算来表示 升维后的空间中的点积结果的数学方式。

重要参数kernel
输入      定义          解决问题        参数gamma     参数degree    参数coef0
linear  线性核         线性              No              No          No
poly    多项式核        偏线性             Yes            Yes         Yes
sigmoid 双曲正切核       非线性            Yes              No         Yes
rbf     高斯径向基       偏非线性           Yes              No          No

linear线性核函数没有参数可调
rbf可以调gamma，gamma可以是任何大于0的浮点数

rbf:一般情况先试试看高斯径向基核函数，它适用于核转换到很高的空间的情况，在各种情况下往往效果都很不错，如果rbf效果不好，那我们再试试看其他的核函数。另外，多项式核函数多被用于图像处理之中

"""

from sklearn.datasets import load_breast_cancer
from sklearn.svm import SVC
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt
import numpy as np
from time import time
import datetime
data = load_breast_cancer()
X = data.data
y = data.target
print(X.shape)
print(np.unique(y))
# plt.scatter(X[:,0],X[:,1],c=y)
# plt.show()

"""
乳腺癌数据集是一个线性数据集，线性核函数跑出来的效果很好。
rbf和sigmoid两个擅长非线性的数据从效果上来看完全不可用。其次，线性核函数的运行速度远远不如非线性的两个核函数。

"""

"""
求解决策边界是靠计算”距离“。SVM严重受到数据量纲的影响。
用describe观察数据，1%和min，99%和max能够看出数据是左偏还是右偏
"""

import pandas as pd
data = pd.DataFrame(X)
# print(data.describe([0.01,0.99]).T)

from sklearn.preprocessing import StandardScaler
X = StandardScaler().fit_transform(X)
data = pd.DataFrame(X)
# print(data.describe([0.01,0.05,0.1,0.25,0.5,0.75,0.9,0.99]).T)


Xtrain, Xtest, Ytrain, Ytest = train_test_split(X,y,test_size=0.3,random_state=420)

Kernel = ["linear"
    ,"poly"       # 多项式核的计算速度非常慢，当设置degree = 1时，变为线性核，速度加快
    ,"rbf","sigmoid"]


# for kernel in Kernel:
#     time0 = time()
#     clf= SVC(kernel = kernel
#              , gamma="auto"
#             , degree = 1        # 默认3，大于1时多线性核跑得很慢
#              , cache_size=5000      # 允许使用电脑的内存数mb
#            ).fit(Xtrain,Ytrain)
#     print("The accuracy under kernel %s is %f" % (kernel,clf.score(Xtest,Ytest)))
#     print(datetime.datetime.fromtimestamp(time()-time0).strftime("%M:%S:%f"))

"""
无量纲化后，所有核函数算法的准确率和运算速度都大幅提升！
"""

# rbf调整gamma，画学习曲线
# score = []
# gamma_range = np.logspace(-10, 1, 50)  # 返回在对数刻度上均匀间隔的数字
# for i in gamma_range:
#     clf = SVC(kernel="rbf", gamma=i, cache_size=5000).fit(Xtrain, Ytrain)
#     score.append(clf.score(Xtest, Ytest))
#
# print(max(score), gamma_range[score.index(max(score))])
# # 0.9766081871345029 0.012067926406393264
# plt.plot(gamma_range, score)
# plt.show()

# 调参——网格搜索

# from sklearn.model_selection import StratifiedShuffleSplit
# from sklearn.model_selection import GridSearchCV
#
# time0 = time()  # 开始计时
#
# # 设置参数范围
# gamma_range = np.logspace(-10,1,20)
# coef0_range = np.linspace(0,5,10)
# # 参数网格
# param_grid = dict(gamma = gamma_range
#                  ,coef0 = coef0_range)
#
# cv = StratifiedShuffleSplit(n_splits=5, test_size=0.3, random_state=420)    # 设置网格搜索样本参数
# # 对poly进行网格搜索
# grid = GridSearchCV(SVC(kernel = "poly",degree=1,cache_size=5000),param_grid=param_grid, cv=cv)
#
# grid.fit(X, y)
#
# print("The best parameters are %s with a score of %0.5f" % (grid.best_params_, grid.best_score_))
# # The best parameters are {'coef0': 0.0, 'gamma': 0.18329807108324375} with a score of 0.96959
# print(datetime.datetime.fromtimestamp(time()-time0).strftime("%M:%S:%f"))

"""
至此各模型结果：
linear:     0.97661
poly:       0.96959, gamma=0.18329807108324375
rbf:        0.97661, gamma=0.012067926406393264
sigmoid:    0.95322
可以看出linear和rbf的效果最优，进行进一步调参选择确定最优模型
"""


"""
硬间隔与软间隔
当两组数据是完全线性可分，我们可以找出一个决策边界使得训练集上的分类误差为0，这两种数据就被称为是存在”硬间隔“的。
当两组数据几乎是完全线性可分的，但决策边界在训练集上存在较小的训练误差，这两种数据就被称为是存在”软间隔“。

使用C惩罚性系数，默认1，必须大于等于0
松弛系数的惩罚项系数。
如果C值设定比较大，那SVC可能会选择边际较小的，能够更好地分类所有训练点的决策边界，不过模型的训练时间也会更长。
如果C的设定值较高，那SVC会尽量最大化边界，决策功能会更简单，但代价是训练的准确度。
换句话说，C在SVM中的影响就像正则化参数对逻辑回归的影响

在实际使用中，C和核函数的相关参数（gamma，degree等等）们搭配，往往是SVM调参的重点。
与gamma不同，C没有在对偶函数中出现，并且是明确了调参目标的，所以我们可以明确我们究竟是否需要训练集上的高精确度来调整C的方向。
默认情况下C为1，通常来说这都是一个合理的参数。 如果我们的数据很嘈杂，那我们往往减小C。
当然，我们也可以使用网格搜索或者学习曲线来调整C的值。

"""

# 调线性核函数
# score = []
# C_range = np.linspace(0.01,30,50)
# for i in C_range:
#     clf = SVC(kernel="linear", C=i, cache_size=5000).fit(Xtrain, Ytrain)
#     score.append(clf.score(Xtest, Ytest))
#
# print(max(score), C_range[score.index(max(score))])
# # 0.9766081871345029 1.2340816326530613
# plt.plot(C_range, score)
# plt.show()


# 换rbf
# score = []
# C_range = np.linspace(0.01, 30, 50)
# for i in C_range:
#     clf = SVC(kernel="rbf", C=i, gamma=
#     0.012067926406393264, cache_size=5000).fit(Xtrain, Ytrain)
#     score.append(clf.score(Xtest, Ytest))
#
# print(max(score), C_range[score.index(max(score))])
# # 0.9824561403508771 6.7424489795918365
# plt.plot(C_range, score)
# plt.show()

# C初步调整后，确定rbf模型效果高于linear，对rbf进一步调参


# 进一步细化
score = []
C_range = np.linspace(6, 8, 50)     # 之前6.13最优，所以选择了5到7
for i in C_range:
    clf = SVC(kernel="rbf", C=i, gamma=
    0.012067926406393264, cache_size=5000).fit(Xtrain, Ytrain)
    score.append(clf.score(Xtest, Ytest))

print(max(score), C_range[score.index(max(score))])
# 0.9824561403508771 6.244897959183674
plt.plot(C_range, score)
plt.show()

"""
最终确定最优和函数为rbf,模型准确率为0.982456
参数为：
gamma = 0.012067926406393264
C = 6.244897959183674
"""